'use client'; import { use, useCallback, useEffect, useState } from 'react'; import Link from 'next/link'; import { Copy, Trash2, AlertTriangle } from 'lucide-react'; import { fetchApi, getDateTime } from '@/lib/utils/client'; import Loading from '@/app/component/Loading'; import Pagination from '@/app/component/Pagination'; import NavTabs from '../../navTabs'; import type { InventoryListResponse, InventoryRow, UseCouponResponse } from '@/types/store'; const PER_PAGE = 30; const MASK_PLACEHOLDER = '* * * * * * * * * * * * * * * *'; function formatExpiryDate(iso: string): string { const d = new Date(iso); const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, '0'); const day = String(d.getDate()).padStart(2, '0'); return `${y}-${m}-${day}`; } export default function InventoryDetailPage({ params }: { params: Promise<{ productID: string }> }) { const { productID } = use(params); const productIdNum = parseInt(productID, 10); const [items, setItems] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(true); const [revealedCodes, setRevealedCodes] = useState>({}); const [justRevealedID, setJustRevealedID] = useState(null); const [copiedID, setCopiedID] = useState(null); const load = useCallback(async () => { setLoading(true); const res = await fetchApi( `/api/store/inventory?productID=${productIdNum}&page=${page}&perPage=${PER_PAGE}`, { silent: true } ); if (res.success && res.data) { setItems(res.data.list); setTotal(res.data.total); } else { setItems([]); setTotal(0); } setLoading(false); }, [productIdNum, page]); useEffect(() => { load(); }, [load]); const handleUse = async (inv: InventoryRow) => { if (inv.isExpired) { return; } const ok = window.confirm('쿠폰을 확인하면 사용 처리되며 환불받을 수 없습니다.\n계속하시겠습니까?'); if (!ok) { return; } const res = await fetchApi(`/api/store/inventory/${inv.id}/use`, { method: 'POST', silent: true }); if (res.success && res.data) { setRevealedCodes(prev => ({ ...prev, [inv.id]: res.data!.code })); setJustRevealedID(inv.id); setItems(prev => prev.map(it => it.id === inv.id ? { ...it, usedAt: res.data!.usedAt, code: res.data!.code } : it )); // 잠시 강조 후 해제 window.setTimeout(() => { setJustRevealedID(curr => (curr === inv.id ? null : curr)); }, 3000); return; } window.alert(res.message || '쿠폰 사용 처리에 실패했습니다.'); }; const handleDelete = async (inv: InventoryRow) => { const ok = window.confirm('보관함에서 삭제하시겠습니까?'); if (!ok) { return; } const res = await fetchApi(`/api/store/inventory/${inv.id}`, { method: 'DELETE', silent: true }); if (res.success) { setItems(prev => prev.filter(it => it.id !== inv.id)); setTotal(prev => Math.max(0, prev - 1)); } else { window.alert(res.message || '삭제에 실패했습니다.'); } }; const handleCopy = async (code: string, id: number) => { try { await navigator.clipboard.writeText(code); setCopiedID(id); window.setTimeout(() => { setCopiedID(curr => (curr === id ? null : curr)); }, 1500); } catch { window.prompt('아래 코드를 복사해주세요:', code); } }; const productName = items[0]?.productName ?? ''; const productThumbnail = items[0]?.productThumbnail ?? null; const gameName = items[0]?.gameName ?? ''; return ( <>
< 보관함으로
{/* 빨간 경고 안내 — 사용 시 환불 불가 */}

쿠폰 코드를 확인하면 즉시 사용 처리되며 환불받을 수 없습니다. 신중하게 확인해주세요.

{!loading && items.length > 0 && (
{productThumbnail && (
{/* eslint-disable-next-line @next/next/no-img-element */} {productName}
)}
{gameName}

{productName}

총 {total.toLocaleString()}장
)} {loading ? ( ) : items.length === 0 ? (
보관함에 이 상품의 쿠폰이 없습니다.
) : ( <>
    {items.map((it) => { const reveal = revealedCodes[it.id] ?? it.code; const expired = it.isExpired; const justReveal = justRevealedID === it.id; const copied = copiedID === it.id; return (
  • {/* 코드 영역 */}
    {reveal ? ( <> e.currentTarget.select()} className={`font-mono w-full sm:max-w-xs border rounded px-3 py-2 text-sm bg-white dark:bg-neutral-900 select-all transition-shadow ${justReveal ? 'border-blue-500 ring-2 ring-blue-200 dark:ring-blue-900' : 'border-neutral-300 dark:border-neutral-700'}`} aria-label='쿠폰 코드' /> {copied && ( 복사됨! )} {justReveal && !copied && ( 방금 사용됨 )} ) : expired ? ( <> 만료 ) : ( <> handleUse(it)} className='font-mono w-full sm:max-w-xs border border-neutral-300 dark:border-neutral-700 rounded px-3 py-2 text-sm bg-neutral-50 dark:bg-neutral-800 text-neutral-400 tracking-wider cursor-pointer hover:border-blue-500 hover:text-neutral-500' aria-label='쿠폰 코드 (확인 버튼을 눌러주세요)' /> )}
    {it.expiresAt && !expired && (

    ~ {formatExpiryDate(it.expiresAt)} 까지 사용 가능

    )}
    {/* 메타 정보 */}
    구매: 구매 {getDateTime(it.acquiredAt)} · 사용: 사용 {it.usedAt ? getDateTime(it.usedAt) : '-'}
    {/* 삭제 */}
  • ); })}
)}
); }